cmake_minimum_required(VERSION 3.20)
project(MCP_CPP VERSION 1.0.0 LANGUAGES CXX)

# Set C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Compiler flags
if(MSVC)
    add_compile_options(/W4 /WX)
    add_definitions(-D_WIN32_WINNT=0x0A00)
else()
    add_compile_options(-Wall -Wextra -Wpedantic -Werror)
    add_compile_options(-march=native)
endif()

# Set output directories
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# Find dependencies
find_package(fmt REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(CURL REQUIRED)
find_package(ZLIB REQUIRED)
find_package(Threads REQUIRED)
find_package(cJSON REQUIRED)

# Optional dependencies
find_package(cppcoro QUIET)
if(NOT cppcoro_FOUND)
    message(STATUS "cppcoro not found, using fallback implementation")
endif()

# Add source files
set(MCP_SOURCES
    src/types.cpp
    src/version.cpp
    src/json/value.cpp
    src/error.cpp
    src/server/tool_manager.cpp
    src/server/resource_manager.cpp
    src/server/prompt_manager.cpp
    src/server/fastmcp.cpp
    src/server/server_session.cpp
    src/server/initialization.cpp
    src/server/lifespan_manager.cpp
    src/transport/transport.cpp
    src/transport/transport_factory.cpp
    src/transport/transport_security.cpp
    src/transport/stdio_transport.cpp
    src/transport/http_transport.cpp
    src/transport/websocket_transport.cpp
    src/transport/sse_transport.cpp
    src/client/client.cpp
    src/client/simple_client.cpp
    src/auth/authenticator.cpp
    src/auth/oauth2.cpp
    src/auth/auth_handler.cpp
)

# Add header files
set(MCP_HEADERS
    include/mcp/config.h
    include/mcp/version.h
    include/mcp/forward.h
    include/mcp/types.h
    include/mcp/json/value.h
    include/mcp/error.h
    include/mcp/server/tool_manager.h
    include/mcp/server/resource_manager.h
    include/mcp/server/prompt_manager.h
    include/mcp/server/fastmcp.h
    include/mcp/server/server_session.h
    include/mcp/transport/transport.h
    include/mcp/transport/stdio_transport.h
    include/mcp/transport/http_transport.h
    include/mcp/transport/websocket_transport.h
    include/mcp/transport/sse_transport.h
    include/mcp/client/client.h
    include/mcp/client/simple_client.h
    include/mcp/auth/authenticator.h
    include/mcp/auth/oauth2.h
    include/mcp/auth/auth_handler.h
)

# Create library
add_library(mcp-cpp SHARED ${MCP_SOURCES} ${MCP_HEADERS})

# Set include directories
target_include_directories(mcp-cpp
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
)

# Link libraries
target_link_libraries(mcp-cpp
    PUBLIC
        fmt::fmt
        nlohmann_json::nlohmann_json
        OpenSSL::SSL
        OpenSSL::Crypto
        CURL::libcurl
        ZLIB::ZLIB
        Threads::Threads
        cjson
)

# Add cppcoro if available
if(cppcoro_FOUND)
    target_link_libraries(mcp-cpp PUBLIC cppcoro::cppcoro)
endif()

# Set properties
set_target_properties(mcp-cpp PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1
    PUBLIC_HEADER "${MCP_HEADERS}"
)

# Add static library
add_library(mcp-cpp-static STATIC ${MCP_SOURCES} ${MCP_HEADERS})
set_target_properties(mcp-cpp-static PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1
    PUBLIC_HEADER "${MCP_HEADERS}"
)

# Static library specific settings
target_include_directories(mcp-cpp-static
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
)

target_link_libraries(mcp-cpp-static
    PUBLIC
        fmt::fmt
        nlohmann_json::nlohmann_json
        OpenSSL::SSL
        OpenSSL::Crypto
        CURL::libcurl
        ZLIB::ZLIB
        Threads::Threads
        cjson
)

if(cppcoro_FOUND)
    target_link_libraries(mcp-cpp-static PUBLIC cppcoro::cppcoro)
endif()

# Add tests
option(BUILD_TESTS "Build tests" OFF)
if(BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

# Add examples
option(BUILD_EXAMPLES "Build examples" OFF)
if(BUILD_EXAMPLES)
    add_subdirectory(examples)
endif()

# Configure package
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)

# Install configuration
install(TARGETS mcp-cpp mcp-cpp-static
    EXPORT mcp-cpp-targets
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

# Install CMake config files
configure_package_config_file(
    cmake/mcp-cpp-config.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/mcp-cpp-config.cmake
    INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mcp-cpp
)

write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/mcp-cpp-config-version.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY SameMajorVersion
)

install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/mcp-cpp-config.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/mcp-cpp-config-version.cmake
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mcp-cpp
)

install(EXPORT mcp-cpp-targets
    FILE mcp-cpp-targets.cmake
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mcp-cpp
)

# Install package files
install(FILES
    README.md
    LICENSE
    DESTINATION ${CMAKE_INSTALL_DATADIR}/mcp-cpp
)

# Configure for different build types
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    add_compile_definitions(DEBUG)
    add_compile_options(-g -O0)
else()
    add_compile_definitions(NDEBUG)
    add_compile_options(-O3 -DNDEBUG)
endif()

# Export package
export(EXPORT mcp-cpp-targets
    FILE ${CMAKE_CURRENT_BINARY_DIR}/mcp-cpp-targets.cmake
)

# Print configuration
message(STATUS "MCP C++ SDK Configuration:")
message(STATUS "  Version: ${PROJECT_VERSION}")
message(STATUS "  Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "  C++ standard: ${CMAKE_CXX_STANDARD}")
message(STATUS "  Install prefix: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "  Build tests: ${BUILD_TESTS}")
message(STATUS "  Build examples: ${BUILD_EXAMPLES}")
message(STATUS "  C++ coroutine support: ${cppcoro_FOUND}")